<script setup lang="ts">
import { onMounted } from "vue"

onMounted(() => {
  const boxEl = document.querySelector('.virtual-box')
  const contentEl = document.querySelector('.virtual-content')
  const listLength = 1000 // 样例数
  let list // 存储根据样例数生成的节点

  const initHeight = () => {
    list = Array.from({ length: listLength }).map(item => ({}))
    list.forEach((item, index) => {
      const divEl = document.createElement('div')
      divEl.classList.add('test-item')
      divEl.id = `id-${index}`
      divEl.innerText = index
      item['el'] = divEl
      item['position'] = 40 * index
    })
    boxEl.style.height = `${listLength * 40}px`
  }
  // 初始化
  initHeight()
  const initEl = document.createElement('div')
  initEl.append(...list.slice(0, 10).map(item => item.el))
  boxEl.innerHTML = initEl.innerHTML
  // 监听滚动事件
  contentEl.addEventListener('scroll', e => {
    const scrollTop = contentEl.scrollTop
    let h
    let a = 0
    let b = listLength - 1
    let i = ~~(listLength / 2)
    let oldI

    // Searching for startIndex
    do {
      oldI = i
      h = list[i].position
      if (h < scrollTop) {
        a = i
      } else if (i < listLength - 1 && list[i + 1].position > scrollTop) {
        b = i
      }
      i = ~~((a + b) / 2)
    } while (i !== oldI)

    const temEl = document.createElement('div')
    const copy = list.slice(i, i+10)
    copy.forEach(item => {
      item['el'].style.transform = `translateY(${40 * i}px)`
    })
    temEl.append(...copy.map(item => item.el))
    boxEl.innerHTML = temEl.innerHTML
  })
})
</script>

<template>
  <div class="virtual-content">
    <div class="virtual-box"></div>
  </div>
</template>

<style scoped lang="scss">
.virtual-content {
  width: 100%;
  height: 200px;
  overflow-y: auto;
  .virtual-box {
    width: 90%;
    overflow: hidden;
    border: 1px solid red;
  }
}
:deep(.test-item) {
  width: 100%;
  height: 40px;
  text-align: center;
  color: deeppink;
  line-height: 40px;
  background: linear-gradient(#0df6ec, #00bebe);
}
</style>